/*
 	Tool for Path Finding Algorithm Testing (ToPFAT) 
 	
    Copyright (C) 2009, Teemu Turunen and Arttu Viljakainen 
    Published under GNU General Public License V3 
    (see http://www.gnu.org/licenses/licenses.html)
    
    This file is part of Tool for Path Finding Algorithm Testing.

    Tool for Path Finding Algorithm Testing is free software: you can 
    redistribute it and/or modify it under the terms of the GNU General 
    Public License as published by the Free Software Foundation, 
    either version 3 of the License, or (at your option) any later version.

    Tool for Path Finding Algorithm Testing is distributed in the hope that it
    will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General 
    Public License for more details.

    You should have received a copy of the GNU General Public License
    along with Tool for Path Finding Algorithm Testing. If not, see         <http://www.gnu.org/licenses/>.
 */

package graph;

import java.awt.Point;

public class GraphFactory {
	
	private int[][] sourceMap;
	private LinkedNodeList<Node> nodes;
	private LinkedEdgeList<Edge> edges;
	
	private GraphFactory(int[][] sourceMap) {
		this.sourceMap = sourceMap;
		nodes = new LinkedNodeList<Node>();
		edges = new LinkedEdgeList<Edge>();
	}
	
	public static Graph generateGraphFrom(int[][] sourceMap) {
		System.out.println("Generating map... ");
		Graph graph = new Graph();
				
		GraphFactory graphFactory = new GraphFactory(sourceMap);
		
		graphFactory.generateNodes();
		graphFactory.AddNeighboursForNodes();
		graphFactory.generateEdgesBetweenNodes();
		
		graph.setSourceMap(graphFactory.sourceMap);
		graph.setNodes(graphFactory.nodes);
		graph.setEdges(graphFactory.edges);
		
		System.out.println("Map generated.");
		return graph;
	}
	
	private void generateNodes() {
		for(int rowIndex = 0; rowIndex < sourceMap.length; rowIndex ++) {
			for(int columnIndex = 0; columnIndex < sourceMap[0].length; columnIndex ++) {
				if(sourceMap[rowIndex][columnIndex] == 0) // TODO Change 0 to enum.
					createANodeWithoutNeighbours(new Point(columnIndex, rowIndex), sourceMap[rowIndex][columnIndex]);
			}
		}
	}
	
	private void createANodeWithoutNeighbours(Point nodeLocation, int type) {
		Node newNode = new Node(nodeLocation, type);
		nodes.add(newNode);
	}
	
	private void AddNeighboursForNodes() {
		for(Node node : nodes) {
			addNeighboursFor(node);
		}
	}
	
	private void addNeighboursFor(Node node) {
		
		Point location = node.getLocation();
		Point north = new Point(location.x, location.y - 1);
		Point south = new Point(location.x, location.y + 1);
		Point east = new Point(location.x + 1, location.y);
		Point west = new Point(location.x - 1, location.y);
		
		Point northEast = new Point(location.x + 1, location.y - 1);
		Point southEast = new Point(location.x + 1, location.y + 1);
		Point southWest = new Point(location.x - 1, location.y + 1);
		Point northWest = new Point(location.x - 1, location.y - 1);

		if(nodes.IsThereANodeAtPoint(north))
			node.addNeighbour(new Node(north, sourceMap[north.y][north.x]));
		if(nodes.IsThereANodeAtPoint(south))
			node.addNeighbour(new Node(south, sourceMap[south.y][south.x]));
		if(nodes.IsThereANodeAtPoint(east))
			node.addNeighbour(new Node(east, sourceMap[east.y][east.x]));
		if(nodes.IsThereANodeAtPoint(west))
			node.addNeighbour(new Node(west, sourceMap[west.y][west.x]));
		
		if(nodes.IsThereANodeAtPoint(northEast) && 
				node.getNeighbours().IsThereANodeAtPoint(north) &&
				node.getNeighbours().IsThereANodeAtPoint(east)) 
					node.addNeighbour(new Node(northEast, sourceMap[northEast.y][northEast.x]));
		if(nodes.IsThereANodeAtPoint(southEast) && 
				node.getNeighbours().IsThereANodeAtPoint(east) &&
				node.getNeighbours().IsThereANodeAtPoint(south)) 
					node.addNeighbour(new Node(southEast, sourceMap[southEast.y][southEast.x]));
		if(nodes.IsThereANodeAtPoint(southWest) && 
				node.getNeighbours().IsThereANodeAtPoint(south) &&
				node.getNeighbours().IsThereANodeAtPoint(west)) 
					node.addNeighbour(new Node(southWest, sourceMap[southWest.y][southWest.x]));
		if(nodes.IsThereANodeAtPoint(northWest) && 
				node.getNeighbours().IsThereANodeAtPoint(north) &&
				node.getNeighbours().IsThereANodeAtPoint(west)) 
					node.addNeighbour(new Node(northWest, sourceMap[northWest.y][northWest.x]));
	}
	
	public void generateEdgesBetweenNodes() {
		for(Node node : nodes) {
			for(Node neighbour : node.getNeighbours()) {
				edges.add(new Edge(node, neighbour, calculateCostBetweenNodes(node, neighbour)));
			}
		}
	}
	
	// TODO Change numbers to enums or so
	public int calculateCostBetweenNodes(Node from, Node to) {
		if(from.getLocation().y == to.getLocation().y || 
				from.getLocation().x == to.getLocation().x)
			return 10;
		else 
			return 14;
	}
	
}
